#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>
#include <resolv.h>
#include "config.h"
#include "http.h"
#include "play.h"
#include "tts.h"


char* appid = "4586440416";
//生成UUID
char* gen_uuid(void)
{
    static char uuid_str[37];
    uuid_t uuid;
    uuid_generate(uuid);
    uuid_unparse(uuid, uuid_str);
    return uuid_str;
}

// base64解码
//base64: 需要解码的base64字符串
//decoded: 解码后的数据
//返回值: 解码后的数据大小
size_t base64_decode(const char* base64, char** decoded)
{
    // 计算解码后的数据大小
    size_t decoded_size = (strlen(base64) / 4 + 1) * 3;
    *decoded = (char*)malloc(decoded_size);
    if (!*decoded)
    {
        return 0;
    }

    // 解码base64字符串
    int size = b64_pton(base64, *decoded, decoded_size);
    if (size < 0)
    {
        return 0;
    }

    return size;
}

//发送请求
//text: 需要转为语音的文本
//返回值: API返回的响应消息，失败返回 NULL
static char* send_request(char* text)
{
    //从配置文件中获取 API 密钥
    cJSON* config = read_config("config.json");
    if (!config) {
        return NULL;
    }

    cJSON* ttstoken = cJSON_GetObjectItem(config, "ttstoken");
    if (!ttstoken) {
        fprintf(stderr, "无法获取ttstoken\n");
        return NULL;
    }

    //printf("%s\n", ttstoken->valuestring);

    char* url = "https://openspeech.bytedance.com/api/v1/tts";

    char* auth;
    asprintf(&auth, "Authorization: Bearer;%s", ttstoken->valuestring);

    //添加请求头部字段
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, auth);
    free(auth);

    //创建请求体
    cJSON* obj = cJSON_CreateObject();

    cJSON* app = cJSON_CreateObject();
    cJSON_AddStringToObject(app, "appid", appid);
    cJSON_AddStringToObject(app, "token", ttstoken->valuestring);
    cJSON_AddStringToObject(app, "cluster", "volcano_tts");
    cJSON_AddItemToObject(obj, "app", app);

    cJSON* user = cJSON_CreateObject();
    cJSON_AddStringToObject(user, "uid", "hqyj");
    cJSON_AddItemToObject(obj, "user", user);

    cJSON* audio = cJSON_CreateObject();
    //发音人
    printf("audio type:");
    cJSON_AddStringToObject(audio, "voice_type", "BV007_streaming"); //灿灿2.0
    //方言
    //cJSON_AddStringToObject(audio, "language", "zh_xian");
    //情感
    // cJSON_AddStringToObject(audio, "emotion", "pleased");
    cJSON_AddNumberToObject(audio, "rate", 16000);
    cJSON_AddItemToObject(obj, "audio", audio);

    cJSON* request = cJSON_CreateObject();
    cJSON_AddStringToObject(request, "reqid", gen_uuid());
    cJSON_AddStringToObject(request, "text", text);
    //SSML
    //cJSON_AddStringToObject(request, "text_type", "ssml");
    cJSON_AddStringToObject(request, "operation", "query");
    cJSON_AddItemToObject(obj, "request", request);

    char* json = cJSON_Print(obj);
    size_t size = strlen(json);
    cJSON_Delete(obj);
    // puts(json);

    return post(url, headers, json, &size);
}

//处理服务器返回的响应消息
//response: API返回的响应消息
//size: 音频数据的大小
//返回值：处理后的语音数据
static char* process_response(char* response, size_t* size)
{
    //解析 JSON 响应
    cJSON *obj = cJSON_Parse(response);
    if (!obj) {
        fprintf(stderr, "解析 JSON 失败: [%s]\n", cJSON_GetErrorPtr());
        return NULL;
    }

    cJSON* code = cJSON_GetObjectItem(obj, "code");
    if (!code)
    {
        fprintf(stderr, "JSON 格式错误: 找不到 'code' 字段\n");
        return NULL;
    }

    if (code->valueint != 3000)
    {
        cJSON* message = cJSON_GetObjectItem(obj, "message");
        if (message)
        {
            fprintf(stderr, "message: %s\n", message->valuestring);
        }
        return NULL;
    }

    cJSON* data = cJSON_GetObjectItem(obj, "data");
    if (!data)
    {
        fprintf(stderr, "JSON 格式错误: 找不到 'data' 字段\n");
        return NULL;
    }

    //对data字段的值进行base64解码
    char* audio = NULL;
    size_t audio_size = base64_decode(data->valuestring, &audio);
    if (audio_size == 0)
    {
        fprintf(stderr, "base64解码失败\n");
        return NULL;
    }

    cJSON_Delete(obj);

    *size = audio_size;
    return audio;
}

//语音合成
void text_to_speech(char* text)
{
    //调用语音合成 API
    char* response = send_request(text);
    if (!response) {
        fprintf(stderr, "调用语音合成 API 失败\n");
        return;
    }

    //puts(response);

    //处理服务器返回的响应消息
    size_t size = 0;
    char* audio = process_response(response, &size);
    if (!audio) {
        return;
    }

    //播放音频
    snd_pcm_t *playback; // PCM设备句柄
    snd_pcm_uframes_t period = 999; // 每个周期的帧数
    char *buffer; // 缓冲区，用于存储从文件中读取的音频数据
    FILE *pcm_file; // 输出PCM文件
    int err; // 用于存储错误码
    // 打开音频数据
    pcm_file = fmemopen(audio, size, "rb");
    if (!pcm_file) {
        perror("Error opening output file");
        return;
    }

    playback = play_open("hw:0,0", SND_PCM_FORMAT_S16_LE, 1, 16000, &period);
    if (!playback)
    {
        fclose(pcm_file);
        return;
    }

    printf("period: %d frames\n", period);

    buffer = (char *) malloc(snd_pcm_frames_to_bytes(playback, period)); // 分配缓冲区
    if (!buffer) {
        perror("malloc");
        play_close(playback);
        fclose(pcm_file);
        return;
    }

    // 录制数据
    while (1) {
        size_t bytes = fread(buffer, 1, snd_pcm_frames_to_bytes(playback, period), pcm_file);
        if (bytes == 0)
        {
            if (ferror(pcm_file))
            {
                perror("fread");
                continue;
            }

            if (feof(pcm_file))
            {
                break;
            }
        }

        snd_pcm_sframes_t frames = snd_pcm_writei(playback, buffer, snd_pcm_bytes_to_frames(playback, bytes));
        if (frames < 0)
        {
            fprintf(stderr, "Error from write: %s\n", snd_strerror(frames));
            snd_pcm_recover(playback, frames, 0);
        }
    }

    // 清理资源
    free(buffer); // 释放缓冲区
    fclose(pcm_file); // 关闭文件
    play_close(playback);

    free(response);
}

// int main()
// {
//     //从标准输入读取文本
//     char line[1024];
//     while (fgets(line, sizeof(line), stdin)) {
//         //去除换行符
//         line[strcspn(line, "\n")] = '\0';

//         //调用语音合成 API
//         text_to_speech(line);
//     }
//     return 0;
// }